/*=====================================================================

  File:      MainFrm.cs
  Summary:   Main form of the DB3D application
  Date:	     May 27, 2006

---------------------------------------------------------------------

  This file is part of the Microsoft SQL Server Code Samples.
  Copyright (C) Microsoft Corporation.  All rights reserved.

This source code is intended only as a supplement to Microsoft
Development Tools and/or on-line documentation.  See these other
materials for detailed information regarding Microsoft code samples.

THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

======================================================= */

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Data;
using System.Data.SqlClient;

namespace Microsoft.Samples.SqlServer.DB3D
{
    public partial class MainFrm : Form
    {
		// current connection string
		string m_connectionString;

        // current wireframe
        int m_wireframeId = -1;

        // wireframe info, or null if the user hasn't switched to the info tab
        DataTable m_wireframeInfo;

        public MainFrm() {
            InitializeComponent();
        }

        #region UI handling

        private void MainFrm_Load(object sender, EventArgs e) {
			wireframeCtrl.AutoScale = autoScaleToolStripMenuItem.Checked;
        }

        private void tbX_Scroll(object sender, EventArgs e) {
            wireframeCtrl.RotateX = (double)tbX.Value / 100.0;
            wireframeCtrl.Refresh();
        }

        private void tbY_Scroll(object sender, EventArgs e) {
            wireframeCtrl.RotateY = (double)tbY.Value / 100.0;
            wireframeCtrl.Refresh();
        }

        private void tbZ_Scroll(object sender, EventArgs e) {
            wireframeCtrl.RotateZ = (double)tbZ.Value / 100.0;
            wireframeCtrl.Refresh();
        }

        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
                         Justification = "Catching Exception to avoid the error from hitting the top-level WinForms error handler")]
        private void cmbWireframeList_SelectionChangeCommitted(object sender, EventArgs e) {
            if(cmbWireframeList.SelectedIndex > -1) {

                // obtain the id of the selected wireframe
                int id = ((Pair<string,int>)cmbWireframeList.SelectedItem).Value2;

                if(-1 == id) {// -1 indicates "refresh list"
                    // reload the list
                    RefreshWireframesList();

                    // re-select originally selected item if possible
                    SelectWireframe(m_wireframeId);
                }
                else {
                    try {
                        // load the new wireframe
                        wireframeCtrl.Wireframe = WireframeDBStorage.Load(m_connectionString, id, new WireframeProgress(WireframeProgressCallback));
                        m_wireframeId = id; // current wireframe

                        // reset rotation
                        tbX.Value = 0;
                        tbY.Value = 0;
                        tbZ.Value = 0;

                        wireframeCtrl.ScaleToFit();
                        SetStatus("Ready ({0} triangles loaded)", wireframeCtrl.Wireframe.Triangles.Length);
                    }
                    catch(Exception ex) {
                        MessageBox.Show(this, "Failed to load wireframe from database: " + ex.Message);
                        wireframeCtrl.Clear();
                    }
                }
            }
        }

        void SetStatus(string text, params object[] args) {
            sbStatusLabel.Text = string.Format(text, args);
        }

        void WireframeProgressCallback(int triangleCount) {
            SetStatus("Processing model ({0} triangles) ...", triangleCount);
        }

        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
                         Justification = "Catching Exception to avoid the error from hitting the top-level WinForms error handler")]
        void RefreshWireframesList() {
            // clear the wireframes combobox and retrieve the new list from
            // WireframeStorage, using the new connection string
            cmbWireframeList.Items.Clear();
            try {
                cmbWireframeList.Items.AddRange(WireframeDBStorage.ListWireframes(m_connectionString));

                // add an special entry to let the user refresh the list
                cmbWireframeList.Items.Add(new Pair<string,int>("<refresh list>", -1));
            }
            catch(Exception ex) {
                MessageBox.Show(this, "Failed to retrieve the list of wireframes: " + ex.Message, "List Wireframes", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        void SelectWireframe(int wireframeId) {
            // look for the wireframe with a given ID in the list of the
            // combo-box control and mark it as selected
            if(-1 != wireframeId) {
                foreach(Pair<string,int> item in cmbWireframeList.Items) {
                    if(wireframeId == item.Value2) {
                        cmbWireframeList.SelectedItem = item;
                        break;
                    }
                }
            }
            else
                cmbWireframeList.SelectedIndex = -1;
        }

        private void tabMainFrm_SelectedIndexChanged(object sender, EventArgs e) {
            // if the user switched to the "info" tab, check to see if we have
            // the wireframes details; if not, get them from the database
            if(tabMainFrm.SelectedIndex == 1) {
                // disable menus that are not available while this tab is active
                copyWireframeToDatabaseToolStripMenuItem.Enabled = false;
                deleteWireframeToolStripMenuItem.Enabled = false;

                LoadWireframeDetailsTable(false);
            }
            else { // tab 0 is selected
                // enable menus that are not available while this tab is active
                copyWireframeToDatabaseToolStripMenuItem.Enabled = true;
                deleteWireframeToolStripMenuItem.Enabled = true;
            }
        }

        void LoadWireframeDetailsTable(bool forceReload) {
            if((null == m_wireframeInfo || forceReload) &&
               null != m_connectionString) {

                // get info from database
                m_wireframeInfo = WireframeDBStorage.ListWireFramesDetailed(m_connectionString);

                // data-bind to the grid
                gridInfo.DataSource = m_wireframeInfo;
                gridInfo.AutoGenerateColumns = true;
            }
        }

        #endregion

        #region File menu

        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
                         Justification = "Catching Exception to avoid the error from hitting the top-level WinForms error handler")]
        private void openFromFileToolStripMenuItem_Click(object sender, EventArgs e) {
            if(DialogResult.OK == dlgOpen.ShowDialog(this)) {
                try {
                    // switch to the wireframe-view tab
                    tabMainFrm.SelectedIndex = 0;

                    // load the wireframe from file
                    wireframeCtrl.Wireframe = WireframeFileStorage.Load(dlgOpen.FileName, new WireframeProgress(WireframeProgressCallback));
                    wireframeCtrl.ScaleToFit();
                    SetStatus("Ready ({0} triangles loaded)", wireframeCtrl.Wireframe.Triangles.Length);

                    // current wireframe is not from database, clean db selection
                    cmbWireframeList.SelectedIndex = -1;
                    m_wireframeId = -1;
                }
                catch(Exception ex) {
                    MessageBox.Show(this, "Wireframe load failed: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    SetStatus("Ready");
                }
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e) {
            Application.Exit();
        }

        #endregion

        #region View menu

        private void autoScaleToolStripMenuItem_Click(object sender, EventArgs e) {
            autoScaleToolStripMenuItem.Checked = !autoScaleToolStripMenuItem.Checked;
            wireframeCtrl.AutoScale = autoScaleToolStripMenuItem.Checked;
        }

        private void refreshToolStripMenuItem_Click(object sender, EventArgs e) {
            if(tabMainFrm.SelectedIndex == 0) { // refresh (reload) current wireframe

                // reload only works for wireframes that come from the database
                if(-1 == m_wireframeId) {
                    MessageBox.Show(this, 
                        "Only wireframes from databases can be refreshed",
                        "Refresh wireframe",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error);
                    return;
                }

                // reload the current wireframe
                wireframeCtrl.Wireframe = WireframeDBStorage.Load(m_connectionString, m_wireframeId, new WireframeProgress(WireframeProgressCallback));

                // re-apply rotation and make sure it fits in screen
                wireframeCtrl.RotateX = tbX.Value / 100.0;
                wireframeCtrl.RotateY = tbY.Value / 100.0;
                wireframeCtrl.RotateZ = tbZ.Value / 100.0;
                wireframeCtrl.ScaleToFit();

                SetStatus("Ready ({0} triangles loaded)", wireframeCtrl.Wireframe.Triangles.Length);
            }
            else { // ==1 -> refresh wireframe details grid
                LoadWireframeDetailsTable(true);
            }
        }

        #endregion

        #region Database menu

        private void connectToolStripMenuItem_Click(object sender, EventArgs e) {
            ConnectDlg dlg = new ConnectDlg();
            if(DialogResult.OK == dlg.ShowDialog(this)) {

                // build connection string - use SqlConnectionStringBuilder to
                // build it securely
                SqlConnectionStringBuilder csbuilder = new SqlConnectionStringBuilder();
                
                if(null != dlg.Server && dlg.Server.Length > 0)
                    csbuilder.DataSource = dlg.Server;

                if(null != dlg.Database && dlg.Database.Length > 0)
                    csbuilder.InitialCatalog = dlg.Database;

                if(dlg.UseIntegratedSecurity)
                    csbuilder.IntegratedSecurity = true;
                else {
                    csbuilder.UserID = dlg.UserId;
                    csbuilder.Password = dlg.Password;
                }

                // Disable connection pooling from this app
                // NOTE: whether or not you want connection pooling is quite application
                // specific. In this particular app we don't want to keep connections
                // opened under the covers. In many other (probably most)
                // scenarios, pooling is *very* beneficial.
                csbuilder.Pooling = false;

                m_connectionString = csbuilder.ToString();

                // Clear the wireframe info that we had from the previous connection, if any
                m_wireframeInfo = null;

                // Load the list of wireframes available in the db we just
                // connected to
                RefreshWireframesList();

                // if we're in the "wireframe details" tab, also load details from db
                LoadWireframeDetailsTable(true);
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
                         Justification = "Catching Exception to avoid the error from hitting the top-level WinForms error handler")]
        private void copyWireframeToDatabaseToolStripMenuItem_Click(object sender, EventArgs e) {

            // can't copy if the user hasn't set a database connection
            if(null == m_connectionString) {
                MessageBox.Show(this,
                    "You need to connect to a database before you can copy wireframes into it. To connect, select 'Connect...' from the 'Database' menu.", 
                    "Copy wireframe to database",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
                return;
            }

            string originalName = wireframeCtrl.Wireframe.Name; // save it in case there's an error

            CopyToDBDlg dlg = new CopyToDBDlg();
            dlg.WireframeName = originalName;
            if(DialogResult.OK == dlg.ShowDialog(this)) {
                wireframeCtrl.Wireframe.Name = dlg.WireframeName;
                try {
                    // load wireframe into database
                    m_wireframeId = WireframeDBStorage.Create(wireframeCtrl.Wireframe, m_connectionString, 
                                        dlg.UseBulkCopy, new WireframeProgress(WireframeProgressCallback));

                    // once it's done set the status back to ready
                    SetStatus("Ready ({0} triangles processed)", wireframeCtrl.Wireframe.Triangles.Length);

                    // refresh the list of wireframes to reflect the new entry and
                    // mark the one we just uploaded as the current one
                    RefreshWireframesList();
                    SelectWireframe(m_wireframeId);
                }
                catch(Exception ex) {
                    // revert to original name
                    wireframeCtrl.Wireframe.Name = originalName;
                    MessageBox.Show(this, "Failed to create a copy of this wireframe in the database: " + ex.Message, "Copy to database", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        private void deleteWireframeToolStripMenuItem_Click(object sender, EventArgs e) {
            // can't copy if the user hasn't set a database connection
            if(null == m_connectionString) {
                MessageBox.Show(this,
                    "You need to connect to a database before you can delete a wireframe. To connect, select 'Connect...' from the 'Database' menu.", 
                    "Delete wireframe",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
                return;
            }

            // obtain the id and name of the currently selected wireframe
            Pair<string,int> item = (Pair<string,int>)cmbWireframeList.SelectedItem;

            if(null == item) {
                MessageBox.Show(this, "No wireframe is currently selected.",
                                "Delete wireframe", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            // ask the user and if ok delete the wireframe from the database
            DialogResult r = MessageBox.Show(this, 
                string.Format("Are you sure you want to delete '{0}' ({1})?", item.Value1, item.Value2), 
                "Delete Wireframe", 
                MessageBoxButtons.YesNo, 
                MessageBoxIcon.Question);

            if(DialogResult.Yes == r) {
                WireframeDBStorage.Delete(m_connectionString, item.Value2);
                wireframeCtrl.Clear();
                RefreshWireframesList();
                cmbWireframeList.Text = "";
                m_wireframeId = -1;
            }
        }

        #endregion

    }
}